unit SQLiteUtils;

interface

uses
  Classes, SysUtils, StrUtils, ComCtrls, DB, DBCtrls, DBGrids,
  RaGSQLite, RaDBOLE, frmSplash;

type

  TSQLiteDBRec = class
  public
    FileName: string;
    Pwd: string;
  end;
  TSQLiteDatabaseStruct = class
  private
    FConnection: TRaSQLiteDB;
    procedure SetConnection(const Value: TRaSQLiteDB);
  public
    TableList: TStringList;
    ViewList:  TStringList;
    constructor Create;
    destructor Destroy; override;
  public
    property Connection: TRaSQLiteDB Read FConnection Write SetConnection;
  end;

  TSQLiteTableStruct = class
  private
    FTableName:  string;
    FConnection: TRaSQLiteDB;
    procedure SetTableName(const Value: string);
  public
    FieldsList:  TStringList;
    IndexList:   TStringList;
    TriggerList: TStringList;
    constructor Create;
    destructor Destroy; override;
  public
    property Connection: TRaSQLiteDB Read FConnection Write FConnection;
    property TableName: string Read FTableName Write SetTableName;
  end;

function GetDatabaseName(AFileName: string): string;
function GenerateCreateTableSQL(ATableName: string; AListView: TListView): string;

function GenerateModifyTableSQL(ATableName: string; AListView: TListView): string;

function GenerateCreateIndexSQL(ATableName, AIndexName, AFieldName, AOrder: string;
  AUniq: boolean): string;
procedure SetAutoWidth(AGrid: TDBGrid);

procedure CopyDataToNewTable(ATableName, AOldTableName: string;
  AQry, ATmpQry: TRaSQLiteQuery; AListview: TListView);
function GenerateBatchSQL(AStr: string): TStringList;

function GetRootIndex(ANode: TTreeNode; ATree: TTreeView): integer;
function GetRootNode(ANode: TTreeNode; ATree: TTreeView): TTreeNode;

function GetDatabaseNameFromSheet(ACaption: string): string;
function GetTableNameFromSheet(ACaption: string): string;
function GetTableNameFromNode(ANode: TTreeNode): string;

function GetTableSQL(AConn: TRaSQLiteDB; ATableName: string): string;
function GetIndexSQL(AConn: TRaSQLiteDB; AIndexName, ATableName: string): string;
function GetTriggerSQL(AConn: TRaSQLiteDB; ATriggerName, ATableName: string): string;
function GetViewSQL(AConn: TRaSQLiteDB; AViewName: string): string;

function GetRealTableNameWithAlias(AAlias, ASQL: string; APOS: integer): string;

function GetFieldNameInList(ASTR: string): string;

// splash form
procedure ShowSplash;
procedure CloseSplash;

var
  splash: TFormSplash = nil;

implementation

uses
  frmMain;

procedure ShowSplash;
begin
  splash:= TFormSplash.Create(nil);
  splash.Show;
  splash.Update;
end;

procedure CloseSplash;
begin
  if Assigned(splash) then
  begin
    splash.Close;
    FreeAndNil(splash);
  end;  
end;

function GetFieldNameInList(ASTR: string): string;
begin
  Result := Trim(LeftStr(ASTR, Pos('(',ASTR)-1));
end;  
  
function GetRealTableNameWithAlias(AAlias, ASQL: string; APOS: integer): string;
var
  i:          integer;
  // pidx:       integer;
  ot:         string;
  typ:        integer;
  s1, s2, sx: string;
  j:          integer;
  k:          integer;
  p1 { , p2 } :     integer;
begin
  //  GetRealTableNameWithAlias
  // search backward and check operation type
  // select, insert, update, delete
  Result := EmptyStr;
  ot     := EmptyStr;
  typ    := -1;
  // pidx := 0;
  for i := APOS - 1 downto 1 do
  begin
    ot := ASQL[i] + ot;
    if Length(ot) >= 6 then
    begin
      // pidx := i;
      if LowerCase(LeftStr(ot, 6)) = 'select' then
      begin
        typ := 0;
        Break;
      end;
      if LowerCase(LeftStr(ot, 6)) = 'insert' then
      begin
        typ := 1;
        Break;
      end;
      if LowerCase(LeftStr(ot, 6)) = 'update' then
      begin
        typ := 2;
        Break;
      end;
      if LowerCase(LeftStr(ot, 6)) = 'delete' then
      begin
        typ := 3;
        Break;
      end;
    end;
  end;
  // can not find operation type
  if typ = -1 then
    Exit;
  // extract table name
  case typ of
    0:
    begin
      // select

      if Pos('from', LowerCase(ot)) > 0 then
      begin
        // after from
        p1 := Pos('from', LowerCase(ot));
        s2 := EmptyStr;
        for i := p1 + 4 to Length(ot) do
        begin
          s2 := s2 + ot[i];
          if Length(s2) >= Length(AAlias) + 1 then
          begin
            if (LowerCase(RightStr(s2, Length(AAlias) + 2)) =
              LowerCase(' ' + AAlias + ' ')) or
              (LowerCase(RightStr(s2, Length(AAlias) + 2)) =
              LowerCase(' ' + AAlias + #13)) or
              (LowerCase(RightStr(s2, Length(AAlias) + 2)) =
              LowerCase(' ' + AAlias + ')')) then
            begin
              if s2[Length(s2)] in [#13, ')',' '] then
                s2 := LeftStr(s2, Length(s2)-1);
              sx     := Trim(Copy(s2, 1, Length(s2) - 2));
              Result := EmptyStr;
              if Pos('join', LowerCase(sx)) > 0 then
              begin
                // HAS JOIN
                for k := length(sx) downto 1 do
                begin
                  if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                    Result := sx[k] + Result
                  else
                    Break;
                end;
              end
              else
              begin
                for k := 1 to length(sx) do
                begin
                  if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                    Result := Result + sx[k]
                  else
                    Break;
                end;
              end;

              Break;
            end
            else
            if LowerCase(RightStr(s2, Length(AAlias) + 1)) =
              LowerCase(' ' + AAlias) then
            begin
              if i = Length(ot) then
              begin
                // find!
                // from xxx x
                // 1234567890
                sx     := Trim(Copy(s2, 1, Length(s2) - 2));
                Result := EmptyStr;

                if Pos('join', LowerCase(sx)) > 0 then
                begin
                  // HAS JOIN
                  for k := length(sx) downto 1 do
                  begin
                    if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                      Result := sx[k] + Result
                    else
                      Break;
                  end;
                end
                else
                begin
                  // normal
                  for k := 1 to length(sx) do
                  begin
                    if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                      Result := Result + sx[k]
                    else
                      Break;
                  end;
                end;
                Break;
              end;
            end;
          end;
        end;

      end
      else
      begin
        // before where
        s1 := EmptyStr;
        for i := APOS to Length(ASQL) do
        begin
          s1 := s1 + ASQL[i];
          if Length(s1) >= 4 then
          begin
            if LowerCase(RightStr(s1, 4)) = 'from' then
            begin
              // find!
              s2 := EmptyStr;
              for j := i + 2 to Length(ASQL) do
              begin
                s2 := s2 + ASQL[j];
                if Length(s2) >= Length(AAlias) + 1 then
                begin
                  if (LowerCase(RightStr(s2, Length(AAlias) + 2)) =
                    LowerCase(' ' + AAlias + ' ')) or
                    (LowerCase(RightStr(s2, Length(AAlias) + 2)) =
                    LowerCase(' ' + AAlias + #13)) or
                    (LowerCase(RightStr(s2, Length(AAlias) + 2)) =
                    LowerCase(' ' + AAlias + ')')) then
                  begin
                    if s2[Length(s2)] in [#13, ')',' '] then
                      s2 := LeftStr(s2, Length(s2)-1);
                    sx     := Trim(Copy(s2, 1, Length(s2) - 2));
                    Result := EmptyStr;
                    if Pos('join', LowerCase(sx)) > 0 then
                    begin
                      // HAS JOIN
                      for k := length(sx) downto 1 do
                      begin
                        if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                          Result := sx[k] + Result
                        else
                          Break;
                      end;
                    end
                    else
                    begin
                      for k := 1 to length(sx) do
                      begin
                        if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                          Result := Result + sx[k]
                        else
                          Break;
                      end;
                    end;

                    Break;
                  end
                  else
                  if LowerCase(RightStr(s2, Length(AAlias) + 1)) =
                    LowerCase(' ' + AAlias) then
                  begin
                    if j = Length(ASQL) then
                    begin
                      // find!
                      // from xxx x
                      // 1234567890
                      sx     := Trim(Copy(s2, 1, Length(s2) - 2));
                      Result := EmptyStr;

                      if Pos('join', LowerCase(sx)) > 0 then
                      begin
                        // HAS JOIN
                        for k := length(sx) downto 1 do
                        begin
                          if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                            Result := sx[k] + Result
                          else
                            Break;
                        end;
                      end
                      else
                      begin
                        // normal
                        for k := 1 to length(sx) do
                        begin
                          if sx[k] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                            Result := Result + sx[k]
                          else
                            Break;
                        end;
                      end;
                      Break;
                    end;
                  end;
                end;
              end;
              Break;
            end;
          end;
        end;
      end;
    end;
    1:
    begin
      //  insert
      if Pos('into',LowerCase(ot)) > 0 then
      begin
        // insert into
        s1 := EmptyStr;
        for i := 12 to Length(ot) do
        begin
          s1 := s1 + ot[i];
          if Length(s1) >= Length(AAlias) then
          begin
            if (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+' ')
              or (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+'(')
              or (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+#13)
            then
            begin
              sx := Trim(s1);
              for j := 1 to Length(sx) do
              begin
                if sx[j] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                  Result := Result + sx[j]
                else
                  Break;
              end;
              Break;
            end;  

          end;  
        end;    
      end
      else
      begin
        // insert
        s1 := EmptyStr;
        for i := 7 to Length(ot) do
        begin
          s1 := s1 + ot[i];
          if Length(s1) >= Length(AAlias) then
          begin
            if (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+' ')
              or (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+'(')
              or (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+#13)
            then
            begin
              sx := Trim(s1);
              for j := 1 to Length(sx) do
              begin
                if sx[j] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                  Result := Result + sx[j]
                else
                  Break;
              end;
              Break;
            end;

          end;  
        end;
      end;
    end;
    2:
    begin
      //  update
      s1 := EmptyStr;
      for i := 7 to Length(ot) do
      begin
        s1 := s1 + ot[i];
        if Length(s1) >= Length(AAlias) then
        begin
          if (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+' ')
              or (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+#13)
            then
            begin
              sx := Trim(s1);
              for j := 1 to Length(sx) do
              begin
                if sx[j] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                  Result := Result + sx[j]
                else
                  Break;
              end;
              Break;
            end;
        end;
      end;
    end;
    3:
    begin
      //  delete
      s1 := EmptyStr;
      for i := pos('from',LowerCase(ot))+4 to Length(ot) do
      begin
        s1 := s1 + ot[i];
        if Length(s1) >= Length(AAlias) then
        begin
          if (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+' ')
              or (LowerCase(RightStr(s1, Length(AAlias)+2)) = ' '+AAlias+#13)
            then
            begin
              sx := Trim(s1);
              for j := 1 to Length(sx) do
              begin
                if sx[j] in ['0'..'9', 'A'..'Z', 'a'..'z', '_'] then
                  Result := Result + sx[j]
                else
                  Break;
              end;
              Break;
            end;
        end;
      end;
    end;
  end;
end;

function GetTableNameFromNode(ANode: TTreeNode): string;
begin
  Result := ANode.Parent.Text;
end;

function GetTableSQL(AConn: TRaSQLiteDB; ATableName: string): string;
begin
  with TRaSQLiteQuery.Create(nil) do
  begin
    Connection := AConn;
    SQL.Text   := 'select * from sqlite_master where type=''table'' and name=' +
      QuotedStr(ATableName);
    Open;
    Result := FieldByName('sql').AsString;
    Close;
    Free;
  end;
end;

function GetIndexSQL(AConn: TRaSQLiteDB; AIndexName, ATableName: string): string;
begin
  with TRaSQLiteQuery.Create(nil) do
  begin
    Connection := AConn;
    SQL.Text   := 'select * from sqlite_master where type=''index'' and name=' +
      QuotedStr(AIndexName) + ' and tbl_name=' + QuotedStr(ATableName);
    Open;
    Result := FieldByName('sql').AsString;
    Close;
    Free;
  end;
end;

function GetTriggerSQL(AConn: TRaSQLiteDB; ATriggerName, ATableName: string): string;
begin
  with TRaSQLiteQuery.Create(nil) do
  begin
    Connection := AConn;
    SQL.Text   := 'select * from sqlite_master where type=''trigger'' and name=' +
      QuotedStr(ATriggerName) + ' and tbl_name=' + QuotedStr(ATableName);
    Open;
    Result := FieldByName('sql').AsString;
    Close;
    Free;
  end;
end;

function GetViewSQL(AConn: TRaSQLiteDB; AViewName: string): string;
begin
  with TRaSQLiteQuery.Create(nil) do
  begin
    Connection := AConn;
    SQL.Text   := 'select * from sqlite_master where type=''view'' and name=' +
      QuotedStr(AViewName);
    Open;
    Result := FieldByName('sql').AsString;
    Close;
    Free;
  end;
end;

function GetTableNameFromSheet(ACaption: string): string;
var
  p1: integer;
begin
  p1     := Pos(']', ACaption);
  Result := Copy(ACaption, p1 + 1, Length(ACaption) - p1);
end;

function GetDatabaseNameFromSheet(ACaption: string): string;
var
  p1, p2: integer;
begin
  p1     := Pos('[', ACaption);
  p2     := Pos(']', ACaption);
  Result := Copy(ACaption, p1 + 1, p2 - p1 - 1);
end;

function GetRootNode(ANode: TTreeNode; ATree: TTreeView): TTreeNode;
var
  r: TTreeNode;
begin
  r := ANode;
  while r.Parent <> nil do
  begin
    r := r.Parent;
  end;
  Result := r;
end;

function GetRootIndex(ANode: TTreeNode; ATree: TTreeView): integer;
var
  rootIdx: integer;
  n:       TTreeNode;
  i: Integer;
begin
  Result  := -1;
  rootIdx := -1;
  for i:=0 to ATree.Items.Count - 1 do
  // for n in ATree.Items do
  begin
    n :=ATree.Items[i];
    if n.Level = 0 then
    begin
      Inc(rootIdx);
      if ANode = n then
      begin
        Result := rootIdx;
        Break;
      end;
    end;
  end;
end;

function GenerateBatchSQL(AStr: string): TStringList;
var
  ALines: TStringList;
  i:      integer;
  idx:    integer;
  hql:    array of string;
  s:      string;
  xloop: Integer;
begin
  // format: @xxxx @xxxx'
  ALines      := TStringList.Create;
  ALines.Text := AStr;
  idx         := -1;
  for i := 0 to ALines.Count - 1 do
  begin
    if ALines[i] <> EmptyStr then
    begin
      if Length(hql) = 0 then
      begin
        SetLength(hql, Length(hql) + 1);
        Inc(idx);
        if ALines[i][1] = '@' then
          hql[idx] := RightStr(ALines[i], Length(ALines[i]) - 1)
        else
          hql[idx] := ALines[i];
      end
      else
      begin
        if ALines[i][1] = '@' then
        begin
          // new line
          SetLength(hql, Length(hql) + 1);
          Inc(idx);
          hql[idx] := RightStr(ALines[i], Length(ALines[i]) - 1);
        end
        else
        begin
          // touch
          hql[idx] := hql[idx] + ' ' + ALines[i];
        end;
      end;
    end;
  end;
  Result := TStringList.Create;
  for xloop:=0 to Length(hql) - 1 do
  begin
    s := hql[xloop];
    Result.Add(s);
  end;  
//  for s in hql do
//    Result.Add(s);
  ALines.Free;
end;

procedure CopyDataToNewTable(ATableName, AOldTableName: string;
  AQry, ATmpQry: TRaSQLiteQuery; AListview: TListView);
var
  fIdx: integer;
  i:    integer;
  tCh:  char;
  fie:  string;
  fv:   string;
  hql:  string;
  //  BlobF: TCnDHHashMap;
  //  ms: TMemoryStream;
begin
  ATmpQry.First;

  fIdx := 0;
  fie  := EmptyStr;
  fv   := EmptyStr;
  hql  := EmptyStr;
  AQry.Close;

  for i := 0 to AListview.Items.Count - 1 do
  begin
    tCh := AListview.Items[i].Caption[Length(AListview.Items[i].Caption)];
    if (tCh = '-') or (tCh = '+') then
    begin
      Inc(fIdx);
      Continue;
    end;
    if ATmpQry.FieldCount > fIdx then
    begin
      fie := fie + AListview.Items[i].Caption + ',';
      fv  := fv + ATmpQry.Fields[fIdx].FieldName + ',';
    end;

    Inc(fIdx);
  end;
  if LeftStr(fie, Length(fie) - 1) <> EmptyStr then
  begin
    // insert into demo2(uid, uname1) select uid, uname from demo
    AQry.SQL.Text := 'insert into ' + ATableName + '(' +
      LeftStr(fie, Length(fie) - 1) + ') select ' + LeftStr(fv, Length(fv) - 1) +
      ' from ' + AOldTableName;
    AQry.ExecSQL;
  end;

end;

function GetDatabaseName(AFileName: string): string;
var
  fn, ext: string;
begin
  fn     := ExtractFileName(AFileName);
  ext    := ExtractFileExt(AFileName);
  Result := LeftStr(fn, Length(fn) - length(ext));
end;

function GenerateCreateTableSQL(ATableName: string; AListView: TListView): string;
var
  item:    TListItem;
  pk:      string;
  pkCount: integer;
  i: Integer;
begin
  //  generate SQL
  pk      := EmptyStr;
  pkCount := 0;
  for i:=0 to AListView.Items.Count - 1 do
  // for item in AListView.Items do
  begin
    item := AListView.Items[i];
    if item.SubItems[1] <> EmptyStr then
      Inc(pkCount);
  end;

  Result := Format('create table %s (', [ATableName]);
  for i:=0 to AListView.Items.Count - 1 do
  // for item in AListView.Items do
  begin
    item := AListView.Items[i];
    Result := Result + Format('%s %s', [item.Caption, item.SubItems[0]]);
    if item.SubItems[1] <> EmptyStr then
    begin
      if pkCount = 1 then
        Result := Result + ' primary key '
      else
        pk     := pk + Format('%s,', [item.Caption]);
    end;

    if item.SubItems[2] <> EmptyStr then
    begin
      if (UpperCase(item.SubItems[0]) = 'NUMERIC') or
        (UpperCase(item.SubItems[0]) = 'INTEGER') or
        (UpperCase(item.SubItems[0]) = 'INT') or
        (UpperCase(item.SubItems[0]) = 'FLOAT') or
        (UpperCase(item.SubItems[0]) = 'CURRENCY') then
        Result := Result + ' default ' + item.SubItems[2]
      else
        Result := Result + ' default ' + QuotedStr(item.SubItems[2]);
    end;
    Result := Result + ',';
  end;
  if pk = EmptyStr then
    Result := LeftStr(Result, Length(Result) - 1)
  else
  begin
    pk     := LeftStr(pk, Length(pk) - 1);
    Result := Result + ' primary key (' + pk + ')';
  end;
  Result := Result + ')';
end;

function GenerateModifyTableSQL(ATableName: string; AListView: TListView): string;
var
  item:    TListItem;
  pk:      string;
  pkCount: integer;
  tCh:     char;
  i: Integer;
begin
  //  generate SQL
  pk      := EmptyStr;
  pkCount := 0;
  for i:=0 to AListView.Items.Count - 1 do
  // for item in AListView.Items do
  begin
    item := AListView.Items[i];
    if item.SubItems[1] <> EmptyStr then
      Inc(pkCount);
  end;

  Result := Format('create table %s (', [ATableName]);
  for i:=0 to AListView.Items.Count - 1 do
  // for item in AListView.Items do
  begin
    item := AListView.Items[i];
    tCh := item.Caption[Length(item.Caption)];
    if tCh = '-' then
      Continue;
    if (tCh = '+') or (tCh = '*') then
      item.Caption := LeftStr(item.Caption, Length(item.Caption) - 1);
    Result := Result + Format('%s %s', [item.Caption, item.SubItems[0]]);
    if item.SubItems[1] <> EmptyStr then
    begin
      if pkCount = 1 then
        Result := Result + ' primary key '
      else
        pk     := pk + Format('%s,', [item.Caption]);
    end;

    if item.SubItems[2] <> EmptyStr then
    begin
      if (UpperCase(item.SubItems[0]) = 'NUMERIC') or
        (UpperCase(item.SubItems[0]) = 'INTEGER') or
        (UpperCase(item.SubItems[0]) = 'INT') or
        (UpperCase(item.SubItems[0]) = 'FLOAT') or
        (UpperCase(item.SubItems[0]) = 'CURRENCY') then
        Result := Result + ' default ' + item.SubItems[2]
      else
        Result := Result + ' default ' + QuotedStr(item.SubItems[2]);
    end;
    Result := Result + ',';
  end;
  if pk = EmptyStr then
    Result := LeftStr(Result, Length(Result) - 1)
  else
  begin
    pk     := LeftStr(pk, Length(pk) - 1);
    Result := Result + ' primary key (' + pk + ')';
  end;
  Result := Result + ')';

end;

function GenerateCreateIndexSQL(ATableName, AIndexName, AFieldName, AOrder: string;
  AUniq: boolean): string;
begin
  Result := Format('create %s index %s on %s (%s %s)',
    [IfThen(AUniq, 'unique', EmptyStr), AIndexName, ATableName, AFieldName, AOrder]);
end;

procedure SetAutoWidth(AGrid: TDBGrid);
var
  i: integer;
  w: integer;
begin
  w := ( {AGrid.Width} FormMain.ppMain.Width - 38) div AGrid.Columns.Count;
  for i := 0 to AGrid.Columns.Count - 1 do
  begin
    AGrid.Columns[i].Width := w;
    if AGrid.Columns[i].Field <> nil then
    begin
      if AGrid.Columns[i].Field.DataType = ftBlob then
        AGrid.Columns[i].ReadOnly := True;
    end;

  end;

end;

{ TSQLiteTableStruct }

constructor TSQLiteTableStruct.Create;
begin
  IndexList   := TStringList.Create;
  TriggerList := TStringList.Create;
  FieldsList  := TStringList.Create;
end;

destructor TSQLiteTableStruct.Destroy;
begin
  IndexList.Free;
  TriggerList.Free;
  FieldsList.Free;
  inherited;
end;

procedure TSQLiteTableStruct.SetTableName(const Value: string);
var
  fd:  TField;
  err: string;
  i: Integer;
begin
  FTableName := Value;
  //  get objects
  with TRaSQLiteQuery.Create(nil) do
  begin
    Connection := FConnection;
    SQL.Text   := 'select * from sqlite_master where tbl_name=' + QuotedStr(FTableName);
    try
      Open;
      First;
      while not EOF do
      begin
        if FieldByName('type').AsString = 'index' then
          IndexList.Add(FieldByName('name').AsString);
        if FieldByName('type').AsString = 'trigger' then
          TriggerList.Add(FieldByName('name').AsString);
        Next;
      end;
    except

    end;

    Close;

    SQL.Text := 'select * from ' + FTableName;
    try
      Open;
      for i:=0 to Fields.Count - 1 do
      begin
        fd := Fields[i];
        FieldsList.Add(fd.FieldName);
      end;  
//      for fd in Fields do
//        FieldsList.Add(fd.FieldName);
    except
      on E: Exception do
        err := e.Message;
    end;

    Close;
    Free;
  end;
end;

{ TSQLiteDatabaseStruct }

constructor TSQLiteDatabaseStruct.Create;
begin
  ViewList  := TStringList.Create;
  TableList := TStringList.Create;
end;

destructor TSQLiteDatabaseStruct.Destroy;
begin
  ViewList.Free;
  TableList.Free;
  inherited;
end;

procedure TSQLiteDatabaseStruct.SetConnection(const Value: TRaSQLiteDB);
begin
  FConnection := Value;
  with TRaSQLiteQuery.Create(nil) do
  begin
    Connection := FConnection;
    SQL.Text   := 'select * from sqlite_master where type=''view'' or type=''table''';
    Open;
    First;
    while not EOF do
    begin
      if FieldByName('type').AsString = 'view' then
        ViewList.Add(FieldByName('name').AsString);
      if FieldByName('type').AsString = 'table' then
        TableList.Add(FieldByName('name').AsString);
      Next;
    end;
    Close;
    Free;
  end;
end;

end.
